package com.github.bitsapling.sapling.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.stp.StpUtil;
import com.github.bitsapling.sapling.config.SiteBasicConfig;
import com.github.bitsapling.sapling.config.TorrentParser;
import com.github.bitsapling.sapling.config.TrackerConfig;
import com.github.bitsapling.sapling.exception.MyCustomException;
import com.github.bitsapling.sapling.result.GraceJSONResult;
import com.github.bitsapling.sapling.result.ResponseStatusEnum;
import com.github.bitsapling.sapling.entity.*;
import com.github.bitsapling.sapling.controller.bo.TorrentUploadBO;
import com.github.bitsapling.sapling.service.*;
import com.github.bitsapling.sapling.utils.IPUtil;
import com.github.bitsapling.sapling.utils.PagedGridResult;
import com.github.bitsapling.sapling.utils.URLEncodeUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.owasp.html.Sanitizers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.owasp.html.PolicyFactory;
import org.springframework.core.io.UrlResource;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;


@RestController    //表示可以被前端调用
@RequestMapping("torrent")
public class TorrentController {
    @Resource
    private TorrentService torrentService;

    @Resource
    private CategoryService categoryService;

    @Resource
    private PeerService peerService;

    @Resource
    private UserService userService;

    @Resource
    private PolicyFactory safeHTMLPolicy;

    @Resource
    private PromotionPolicyService promotionPolicyService;

    @Resource
    private SettingService settingService;

    @Resource
    private TagService tagService;

    @Resource
    @Qualifier("torrentsDirectory")
    private File torrentsDirectory;

    @Resource
    @Qualifier("imgDirectory")
    private File imgDirectory;

    @Resource
    private AuthenticationService authenticationService;

    @Resource
    private HttpServletRequest request;

    @Resource
    private imgPathService imgPathService;

    @PostMapping("upload")
    @SaCheckPermission(value = "torrent:upload", orRole = "admin")
    public GraceJSONResult upload(@Valid TorrentUploadBO torrentUploadBO) throws Exception{
        // 防止跨站脚本（XSS）攻击
        // 使用内置的策略来创建一个sanitizer
        torrentUploadBO.setDescription(safeHTMLPolicy.sanitize(torrentUploadBO.getDescription()));
        User user = userService.getUserById(StpUtil.getLoginIdAsLong());
        Category category = categoryService.getCategoryBySlug(torrentUploadBO.getCategory());
        PromotionPolicy promotionPolicy = promotionPolicyService.getDefaultPromotionPolicy();
        SiteBasicConfig siteBasicConfig = settingService.get(SiteBasicConfig.getConfigKey(), SiteBasicConfig.class);
        if (category == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.CATEGORY_ERROR);
        }
        if (user == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_STATUS_ERROR);
        }
        // TODO 匿名目前没用
        String publisher = user.getUsername();
        String publisherUrl = siteBasicConfig.getSiteBaseURL() + "/user/" + user.getId();
        if (torrentUploadBO.isAnonymous()) {
            //StpUtil.checkPermission("torrent:publish_anonymous");
            publisher = "Anonymous";
            publisherUrl = siteBasicConfig.getSiteBaseURL();
        }
        List<String> tagList = torrentUploadBO.getTag() != null ? torrentUploadBO.getTag() : new ArrayList<>();
        // 将 tagList 拼接成以逗号分隔的字符串
        String tagString = String.join(",", tagList);
        try {
            TorrentParser parser = new TorrentParser(torrentUploadBO.getFile().getBytes(), true);
            parser.rewriteForTracker(siteBasicConfig.getSiteName(), publisher, publisherUrl);
            String infoHash = parser.getInfoHash();
            if (torrentService.getTorrentByHash(infoHash) != null) {
                return GraceJSONResult.errorCustom(ResponseStatusEnum.TORRENT_ALREADY_EXIST_ERROR);
            }
            // 保存种子文件到本地
            Files.write(new File(torrentsDirectory, infoHash + ".torrent").toPath(), parser.save());

            //检测图片是否存在
            if (torrentUploadBO.getImg() != null && !torrentUploadBO.getImg().isEmpty()) {
                imagePath imgPath = new imagePath();
                // 保存图片文件到本地
                String originalFilename = torrentUploadBO.getImg().getOriginalFilename();
                String suffix = "";
                if (originalFilename != null) {
                    // 2. 获取文件后缀名
                    int dotIndex = originalFilename.lastIndexOf('.');
                    if (dotIndex > 0 && dotIndex < originalFilename.length() - 1) {
                        suffix = originalFilename.substring(dotIndex + 1).toLowerCase();
                    }
                }

                Files.write(new File(imgDirectory, infoHash+"."+suffix).toPath(), torrentUploadBO.getImg().getBytes());

                // 保存图片信息到数据库
                imgPath.setImgName(infoHash);
                imgPath.setImgType(suffix);
                imgPath.setImgPath(new File(imgDirectory, infoHash+"."+suffix).toPath());
                imgPathService.createOrUpdate(imgPath);
            }
            imagePath imgPath = imgPathService.findImagePathByname(infoHash);


            // 保存种子信息到数据库
            Torrent torrent = new Torrent();
            torrent.setInfoHash(infoHash);
            torrent.setUserId(user.getId());
            torrent.setTitle(torrentUploadBO.getTitle());
            torrent.setSize(parser.getTorrentFilesSize());
            torrent.setCategoryId(category.getId());
            torrent.setTags(tagString);
            torrent.setDescription(torrentUploadBO.getDescription());
            torrent.setCreateTime(LocalDateTime.now());
            torrent.setUpdatedTime(LocalDateTime.now());
            torrent.setUnderReview(StpUtil.hasPermission("torrent:bypass_review"));
            torrent.setPromotionPolicyId(promotionPolicy.getId());
            torrent.setAnonymous(torrentUploadBO.isAnonymous());
            //设置默认图片
            torrent.setImgPath(0L);
            if (imgPath != null) {
                torrent.setImgPath(imgPath.getId());
            }
            torrentService.createOrUpdate(torrent);
            return GraceJSONResult.ok("种子上传成功！");
        } catch (MyCustomException e) {
            return GraceJSONResult.errorCustom(e.getResponseStatusEnum());
        }
    }

    @GetMapping("/download/{info_hash}")
    @SaCheckPermission(value = "torrent:download", orRole = "admin")
    public HttpEntity<?> download(@PathVariable("info_hash") String infoHash, @RequestParam @NotNull Map<String, String> params) throws Exception {
        User user;
        if (params.containsKey("passkey")) {
            user = authenticationService.authenticate(params.get("passkey"), IPUtil.getRequestIp(request));
        } else {
            user = userService.getUserById(StpUtil.getLoginIdAsLong());
        }
        if (user == null) {
            throw new MyCustomException(ResponseStatusEnum.NO_TICKET_ERROR);
        }
        TrackerConfig trackerConfig = settingService.get(TrackerConfig.getConfigKey(), TrackerConfig.class);
        if (StringUtils.isEmpty(infoHash)) {
            throw new MyCustomException(ResponseStatusEnum.TORRENT_HASH_ERROR);
        }
        Torrent torrent = torrentService.getTorrentByHash(infoHash);
        if (torrent == null) {
            throw new MyCustomException(ResponseStatusEnum.TORRENT_NOT_EXIST_ERROR);
        }
        if (!torrent.getUnderReview()) {
            throw new MyCustomException(ResponseStatusEnum.TORRENT_NOT_REVIEW_ERROR);
        }
        File torrentFile = new File(torrentsDirectory, infoHash + ".torrent");
        if (!torrentFile.exists()) {
            throw new MyCustomException(ResponseStatusEnum.TORRENT_LOST_ERROR);
        }
        TorrentParser parser = new TorrentParser(Files.readAllBytes(torrentFile.toPath()), false);
        parser.rewriteForUser(trackerConfig.getTrackerURL(), userService.getPasskeyById(torrent.getUserId()), user);

        String fileName = "[" + trackerConfig.getTorrentPrefix() + "] " + torrent.getTitle() + ".torrent";
        HttpHeaders header = new HttpHeaders();
        header.set(HttpHeaders.CONTENT_TYPE, "application/x-bittorrent");
        header.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncodeUtil.urlEncode(fileName, false));
        return new HttpEntity<>(parser.save(), header);
    }

    @GetMapping("/list")
    public GraceJSONResult list() {
        return GraceJSONResult.ok(torrentService.queryAllReviewed());
    }


    /**
     * 查看单个种子
     * @param infoHash 种子的 infoHash
     * @return 单个种子信息
     */
    @GetMapping("/{info_hash}")
    public GraceJSONResult getTorrentByHash(@PathVariable("info_hash") String infoHash) {
        if (StringUtils.isEmpty(infoHash)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.TORRENT_HASH_ERROR);
        }
        Torrent torrent = torrentService.getTorrentByHash(infoHash);
        if (torrent == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.TORRENT_NOT_EXIST_ERROR);
        }
        return GraceJSONResult.ok(torrent);
    }

    /**
     * 通过数据库主键id获取单个种子信息
     * @param id 种子的数据库主键id
     * @return 单个种子信息
     */
    @GetMapping("/id/{id}")
    public GraceJSONResult getTorrentById(@PathVariable("id") Long id) {
        if (id == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_PARAMS_ERROR);
        }
        Torrent torrent = torrentService.getTorrentById(id);
        if (torrent == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.TORRENT_NOT_EXIST_ERROR);
        }
        return GraceJSONResult.ok(torrent);
    }

    @GetMapping("/img/{info_hash}")
    public ResponseEntity<UrlResource> getTorrentImg(@PathVariable("info_hash") String infoHash) {
        Torrent torrent=torrentService.getTorrentByHash(infoHash);
        imagePath imgPath=imgPathService.getImagePath(torrent.getImgId());
        try {
            Path path= Paths.get(imgPath.getPath());
            UrlResource resource = new UrlResource(path.toUri());
            // 检查文件是否存在
            if (!resource.exists() || !resource.isReadable()) {
                return ResponseEntity.notFound().build();
            }
            String imgtype=getContentType(imgPath.getImgType());
            return ResponseEntity.ok()
                    .contentType(MediaType.parseMediaType(imgtype))
                    .body(resource);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    private String getContentType(String filename) {
        switch (filename) {
            case "jpg": case "jpeg": return "image/jpeg";
            case "png": return "image/png";
            case "gif": return "image/gif";
            case "webp": return "image/webp";
            default: return "application/octet-stream";
        }
    }
    /**
     * 审核种子
     * @param infoHash 种子的 infoHash
     * @param isApproved 审核结果，0 表示不通过，1 表示通过
     * @return 审核操作结果
     */
    @PostMapping("/review/{info_hash}")
    @SaCheckPermission(value = "torrent:review", orRole = "admin")
    public GraceJSONResult reviewTorrent(@PathVariable("info_hash") String infoHash, @RequestParam int isApproved) {
        if (isApproved != 0 && isApproved != 1) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_PARAMS_ERROR);
        }
        try {
            // 调用服务层方法进行审核操作
            boolean result = torrentService.reviewTorrent(infoHash, isApproved);
            if (result) {
                return GraceJSONResult.ok("种子状态更新成功");
            } else {
                return GraceJSONResult.errorCustom(ResponseStatusEnum.TORRENT_NOT_EXIST_ERROR);
            }
        } catch (Exception e) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR);
        }
    }

    @GetMapping("/review/list")
    @SaCheckPermission(value = "torrent:review", orRole = "admin")
    public GraceJSONResult reviewList() {
        return GraceJSONResult.ok(torrentService.queryAllUnreviewed());
    }

    @GetMapping("/ShareIndex")
    public GraceJSONResult shareIndex() {
        User user = userService.getUserById(StpUtil.getLoginIdAsLong());
        if (user == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_STATUS_ERROR);
        }
        float shareIndex = userService.calculateShaereIndex(user.getId());
        return GraceJSONResult.ok(shareIndex);
    }

    @GetMapping("/MagicIndex")
    public GraceJSONResult magicIndex() {
        User user = userService.getUserById(StpUtil.getLoginIdAsLong());
        if (user == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_STATUS_ERROR);
        }
        float magicIndex = userService.calculateMagicIndex(user.getId());
        return GraceJSONResult.ok(magicIndex);
    }


    @PostMapping("/delete/{info_hash}")
    @SaCheckPermission(value = "user:invite", orRole = "admin")
    public GraceJSONResult deleteTorrent(@PathVariable("info_hash") String infoHash) {
        Torrent torrent = torrentService.getTorrentByHash(infoHash);
        if (torrent == null) {
            throw new MyCustomException(ResponseStatusEnum.TORRENT_NOT_EXIST_ERROR);
        }

        boolean success=torrentService.deleteTorrent(torrent);
        if (success) {
            return GraceJSONResult.ok("删除成功");
        }
        return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_OPERATION_ERROR);
    }


    /**
     * 根据类别 slug 获取对应类别的种子列表
     * @param slug 种子类别名称
     * @return 对应类别的种子列表
     */
    @GetMapping("/{slug}/list")
    public GraceJSONResult getTorrentsByCategory(@PathVariable String slug,
                                                 @RequestParam(defaultValue = "1") int page,
                                                 @RequestParam(defaultValue = "10") int pageSize) {
        // 根据 slug 获取类别对象
        Category category = categoryService.getCategoryBySlug(slug);
        if (category == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.CATEGORY_ERROR);
        }
        PagedGridResult result = torrentService.getTorrentsByCategoryId(category.getId(), page, pageSize);
        return GraceJSONResult.ok(result);
    }


    /**
     * 模糊搜索种子，根据用户传入的字符串在 tags 和 description 字段进行匹配
     * @param keyword 用户输入的搜索关键词
     * @return 包含匹配种子列表的 JSON 结果
     */
    @GetMapping("/search")
    public GraceJSONResult searchTorrents(@RequestParam String keyword) {
        if (StringUtils.isBlank(keyword)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_PARAMS_ERROR);
        }
        List<Torrent> torrents = torrentService.searchTorrents(keyword);
        return GraceJSONResult.ok(torrents);
    }

}
